#include "oh/OHRenderNode.h"
#include "oh/OHDrawingAPI.h"
#include "oh/OHEnv.h"
#include <sys/stat.h>
#include <native_drawing/drawing_record_cmd.h>

#include "include/core/SkPicture.h"
#include "src/core/SkTraceEvent.h"
#include "src/core/SkTraceEventCommon.h"
#include "include/utils/SkParsePath.h"

#include "uv.h"

napi_ref OHRenderNode::gNodeConstructorRef = nullptr;
double OHRenderNode::gVP2PXRatio = 3.125;
bool OHRenderNode::gEnableRasterCache = false;
std::atomic<int> OHRenderNode::gNodeNextId = 0;

void OHRenderNode::Destructor(napi_env env, void *nativeObject, [[maybe_unused]] void *finalize_hint) {
    delete reinterpret_cast<OHRenderNode *>(nativeObject);
}

napi_value OHRenderNode::CreateJsNode() {
    napi_env env = OHEnv::GetInstance()->GetEnv();
    if (env == nullptr || gNodeConstructorRef == nullptr) {
        return nullptr;
    }
    napi_value constructor;
    napi_status status = napi_get_reference_value(env, gNodeConstructorRef, &constructor);
    if (status != napi_ok) {
        return nullptr;
    }
    
    napi_value instance = nullptr;
    status = napi_new_instance(env, constructor, 0, nullptr, &instance);
    if (status != napi_ok) {
        return nullptr;
    }
    return instance;
}

OHRenderNode::OHRenderNode(): OHRenderNode(CreateJsNode()) {
}

OHRenderNode::OHRenderNode(napi_value jsNode) {
    napi_env env = OHEnv::GetInstance()->GetEnv();
    if (!jsNode || !env) {
        return;
    }
    
    napi_value napi_ptr, pivot;
    napi_create_external(env, this, nullptr, nullptr, &napi_ptr);
    napi_status status = napi_set_named_property(env, jsNode, "OHRenderNodePtr", napi_ptr);
    status = napi_create_reference(env, jsNode, 1, &fJsObject);

    napi_value napi_zero;
    napi_get_named_property(env, jsNode, "pivot", &pivot);
    napi_create_double(env, 0.0, &napi_zero);
    status = napi_set_named_property(env, pivot, "x", napi_zero);
    status = napi_set_named_property(env, pivot, "y", napi_zero);
    status = napi_set_named_property(env, jsNode, "pivot", pivot);
    
    char id_str[100];
    snprintf(id_str, 99, "%d", fNodeId);
    id_str[99] = 0;
    napi_value strValue;
    napi_create_string_utf8(env, id_str, strlen(id_str), &strValue);
    status = napi_set_named_property(env, jsNode, "label", strValue);

    // use for debug
//    napi_value napi_false;
//    napi_get_boolean(env, false, &napi_false);
//    napi_set_named_property(env, instance, "clipToFrame", napi_false);

//    status = napi_wrap(env, instance, this, OHRenderNode::NoneDestructor, nullptr, &fJsObject);
    if (status != napi_ok) {
      return;
    }
    fHasRef = true;
    fStatusOK = true;
}

OHRenderNode::~OHRenderNode() {
    napi_value napi_ptr;
    auto env = OHEnv::GetInstance()->GetEnv();
    napi_handle_scope scope;
    napi_open_handle_scope(env, &scope);

    // @todo 可能node已经销毁了，但是node的js对象还在访问使用
    napi_create_external(env, nullptr, nullptr, nullptr, &napi_ptr);
    napi_set_named_property(env, getLocalJsObject(), "OHRenderNodePtr", napi_ptr);

    if (fAsyncTask != nullptr) {
        // fAsyncTask will be use after uv_close and cannot be deleted direct.
        uv_close((uv_handle_t*)fAsyncTask, [](uv_handle_t* handle) {
            delete handle;
        });
        fAsyncTask = nullptr;
    }
    
    for (auto& node: fChildList) {
        auto picture = node->getPicture();
        if (picture && node->fVisible) {
            picture->recycleNode(node);
        }
        node->fVisible = false;
        // father node is destroying
        node->setParent(nullptr);
    }

    if (fHasRef) {
        napi_delete_reference(env, fJsObject);
        fHasRef = false;
    }
    if (fRecorder != nullptr) {
        delete fRecorder;
        fRecorder = nullptr;
    }
    napi_close_handle_scope(env, scope);
}

std::shared_ptr<OHRenderNode> OHRenderNode::makeClone() {
    auto root_node = CreateNormalNode();
    if (!root_node) {
        return nullptr;
    }
    root_node->fFatherMatrixHasChanged = true;
    root_node->fClipShapeHasChanged = true;
    root_node->fNowFrameHasChanged = true;
    root_node->fRealFrameHasChanged = true;
    root_node->fFatherFrameHasChanged = true;
    root_node->fContentHasChanged = true;

    root_node->fNoLimitSize = fNoLimitSize;
    root_node->fNowFrame = fNowFrame;
    root_node->fRealFrame = fRealFrame;
    root_node->fFatherMatrix = fFatherMatrix;
    root_node->fFatherMatrixInv = fFatherMatrixInv;
    root_node->fFatherFrame = fFatherFrame;
    root_node->fNowPaintArea = fNowPaintArea;

    root_node->fHasClip = fHasClip;
    root_node->fNeedClip = fNeedClip;
    root_node->fNowClipShape = fNowClipShape;
    root_node->fPendingDiffShape = fPendingDiffShape;
    
    root_node->fNodeContentHash = fNodeContentHash;
    root_node->fPictureTreeHash = fPictureTreeHash;
    root_node->fPictureChildNum = fPictureChildNum;
    root_node->fNeedDelayChildAppend = fNeedDelayChildAppend;
    
    root_node->fDeviceClipShape =fDeviceClipShape;

    root_node->fNodePicture = fNodePicture;
    root_node->fPictureCmd = fPictureCmd;
    root_node->fPictureTreeCmdCache = fPictureTreeCmdCache;
    
    root_node->fNodeDrawCost = fNodeDrawCost;
    root_node->fPictureDrawCost = fPictureDrawCost;
    
    root_node->fForbidRasterCache = fForbidRasterCache;
    
    root_node->fIsInRasterCache = false;
    root_node->fReadyToCreateRasterCache = false;
    root_node->fRasterCacheStateHasChange = false;
    root_node->fCacheReadyFrameCnt = 0;
    
    root_node->fScaleRatio = fScaleRatio;
    root_node->fFatherScaleRatio = fFatherScaleRatio;
    
    root_node->fMaxDeviceFrame = fMaxDeviceFrame;
    
    root_node->fNodeRedrawCnt = 0;
    
    
    if (fNodePicture) {
        fNodePicture->cacheNode(root_node);
    }
    
    root_node->restartChildAdd();
    for (auto node: fChildList) {
        auto clone_node = node->makeClone();
        root_node->appendChild(clone_node);
        clone_node->setParent(root_node.get());
    }
    root_node->finishChildAdd();
    return root_node;
}

void OHRenderNode::dispose() {
    if (fParent) {
        fParent->removeChild(this);
    }
    for (auto child: fChildList) {
        child->fParent = nullptr;
    }
    fParent = nullptr;
}

napi_value OHRenderNode::setLiveWithJs() {
    auto env = OHEnv::GetInstance()->GetEnv();
    
    auto value = getLocalJsObject();
    napi_status status = napi_add_finalizer(env, value, this, OHRenderNode::Destructor, nullptr, nullptr);
    if (status != napi_ok) {
        return nullptr;
    }

    uv_loop_t* loop;
    fAsyncTask = new uv_async_t;
    fAsyncTask->data = this;
    napi_get_uv_event_loop(env, &loop);
    uv_async_init(loop, fAsyncTask, OHRenderNode::RenderNodeDoRedraw);
    
    if (fHasRef) {
        uint32_t refCount = 0;
        napi_reference_unref(env, fJsObject, &refCount);
    }
    return value;
}

void OHRenderNode::registerCallback(void *context, NodeDrawCallback callback) {
    fCallbackContext = context;
    fCallback = callback;
}

SkRect OHRenderNode::getSuitFrameSize() {
    SkRect min_child_frame;
    SkRect now_rect = fNowFrame;
    if (!fFatherMatrixInv.mapRect(&min_child_frame, fFatherFrame)) {
        return fRealFrame;
    }
    if (fHasClip && fNeedClip) {
        SkRect min_clip_area = fNowClipRect;
        min_child_frame.intersect(min_clip_area);
    }
    if (fNoLimitSize && (!fCanUseClipAsFrame || !fHasClip)) {
        if (!fNowFrame.contains(min_child_frame)) {
            // extend the size
            SkRect merged = now_rect;
            merged.join(min_child_frame);

            float left = std::floor(merged.left() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;
            float top = std::floor(merged.top() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;

            float right = std::ceil(merged.right() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;
            float bottom = std::ceil(merged.bottom() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;

            now_rect = SkRect::MakeLTRB(left, top, right, bottom);
        } else {
            // shrink the size
//            float left = std::floor(min_child_frame.left() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;
//            float top = std::floor(min_child_frame.top() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;
//
//            float right = std::ceil(min_child_frame.right() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;
//            float bottom = std::ceil(min_child_frame.bottom() / NODE_SIZE_ALIGNMENT) * NODE_SIZE_ALIGNMENT;
//
//            now_rect = SkRect::MakeLTRB(left, top, right, bottom);
        }
    } else if (fNoLimitSize && fCanUseClipAsFrame && fHasClip) {
        now_rect = fNowClipRect;
    } else {
        // has limit size
        if (!fNowFrame.contains(min_child_frame)) {
            // avoid shrink => don't change node size frequently.
            now_rect = fNowFrame;
            now_rect.join(fRealFrame);
        } else {
            // bigger than father node => needn't change node size
        }
    }
    return now_rect;
}

// return true if fFatherMatrix is changed (expect Identity)
bool OHRenderNode::updateFatherMatrix(SkMatrix &fatherMatrix) {
    bool old_is_identity = false;
    if (fFatherMatrix.isIdentity()) {
        old_is_identity = true;
    }
    if (!fFatherMatrix.approximatelyEquals(fatherMatrix)) {
        fFatherMatrixHasChanged = true;
        fFatherMatrix = fatherMatrix;
        if (fatherMatrix.invert(&fFatherMatrixInv)) {
            fFatherMatrix = fatherMatrix;
        } else {
            SkDebugf("error! matrix cannot invert!");
        }
        // fFatherMatrix changed => need update scale
        updateScaleRatio(fFatherScaleRatio);
        if (old_is_identity) {
            return false;
        } else {
            return true;
        }
    }
    return false;
}

void OHRenderNode::updateFatherNodeFrame(SkRect &fatherFrame) {
    if (!fFatherFrame.approximatelyEquals(fatherFrame, 1e-1)) {
        fFatherFrameHasChanged = true;
        fFatherFrame = fatherFrame;
    }
}

void OHRenderNode::unsetClipArea() {
    fNeedClip = false;
    updateClipAreaWithFrameChange();
    // fNeedClip will be set to true in updateClipArea.
    fNeedClip = false;
}

void OHRenderNode::updateClipAreaWithFrameChange() {
    if (fHasClip && !fNeedClip) {
        SkClipShape shape = fNowFrame;
        updateClipArea(shape);
    }
}

void OHRenderNode::updateDeviceClip(SkClipShape &clip) {
    fDeviceClipShape = clip;
}

void OHRenderNode::updateClipArea(SkClipShape &clip) {
    if (std::holds_alternative<std::monostate>(fNowClipShape)) {
        fNowClipShape = clip;
        fClipShapeHasChanged = true;
    } else {
        if (std::holds_alternative<SkRect>(fNowClipShape) && std::holds_alternative<SkRect>(clip)) {
            const SkRect &clipNow = std::get<SkRect>(fNowClipShape);
            const SkRect &clipIn = std::get<SkRect>(clip);
            if (!clipNow.approximatelyEquals(clipIn, 1e-1)) {
                fNowClipShape = clip;
                fClipShapeHasChanged = true;
            }
        } else if (std::holds_alternative<SkRRect>(fNowClipShape) && std::holds_alternative<SkRRect>(clip)) {
            const SkRRect &clipNow = std::get<SkRRect>(fNowClipShape);
            const SkRRect &clipIn = std::get<SkRRect>(clip);
            if (!clipNow.approximatelyEquals(clipIn, 1e-1)) {
                fNowClipShape = clip;
                fClipShapeHasChanged = true;
            }
        } else if (std::holds_alternative<SkPath>(fNowClipShape) && std::holds_alternative<SkPath>(clip)) {
            const SkPath &clipNow = std::get<SkPath>(fNowClipShape);
            const SkPath &clipIn = std::get<SkPath>(clip);
            if (clipNow != clipIn) {
                fNowClipShape = clip;
                fClipShapeHasChanged = true;
            }
        } else {
            fNowClipShape = clip;
            fClipShapeHasChanged = true;
        }
        if (fClipShapeHasChanged) {
            // clip change many times => size may change many times
            fCanUseClipAsFrame = false;
        }
    }

    if (std::holds_alternative<SkRect>(fNowClipShape)) {
        const SkRect &clipNow = std::get<SkRect>(fNowClipShape);
        fNowClipRect = clipNow;
    } else if (std::holds_alternative<SkRRect>(fNowClipShape)) {
        const SkRRect &clipNow = std::get<SkRRect>(fNowClipShape);
        fNowClipRect = clipNow.rect();
    } else if (std::holds_alternative<SkPath>(fNowClipShape)) {
        const SkPath &clipNow = std::get<SkPath>(fNowClipShape);
        fNowClipRect = clipNow.getBounds();
    } else {
        assert(0);
    }
    
//    fNodeDrawCost = (1 + 1 + fScaleRatio) * fNowClipRect.width() * fNowClipRect.height();

    fNeedClip = true;
    fHasClip = true;
}

void OHRenderNode::updatePendingDiffShape(const SkClipShape& shape) {
    if (fHasClip) {
        fPendingDiffShape = shape;
        fClipShapeHasChanged = true;
    }
}

void OHRenderNode::updateRelativeLocation() {
    SkPoint new_offset = {fNowFrame.x() - fFatherFrame.x(), fNowFrame.y() - fFatherFrame.y()};
    SkSize new_size = {fNowFrame.width(), fNowFrame.height()};
    if (new_offset != fRelativeOffset) {
        fRelativeOffset = new_offset;
        fPositionNeedUpdate = true;
    }
    if (new_size != fNowFrameSize) { 
        fNowFrameSize = new_size;
        fSizeNeedUpdate = true;
    }
}

void OHRenderNode::updateRelativeClipArea() {
    SkClipShape shape;
    if (!std::holds_alternative<std::monostate>(fPendingDiffShape)) {
        fNowClipShape = SkCanvas::differenceShape(fNowFrame, fPendingDiffShape);
        // fPendingDiffShape needs to be reset to avoid incorrectly updating fNowClipShape when rendering with a
        // different clip region.
        fPendingDiffShape = std::monostate();
    }
    if (std::holds_alternative<SkRect>(fNowClipShape)) {
        shape = std::get<SkRect>(fNowClipShape).makeOffset(-fNowFrame.left(), -fNowFrame.top());
    } else if (std::holds_alternative<SkRRect>(fNowClipShape)) {
        shape = std::get<SkRRect>(fNowClipShape).makeOffset(-fNowFrame.left(), -fNowFrame.top());
    } else if (std::holds_alternative<SkPath>(fNowClipShape)) {
        SkPath relative_clip = std::get<SkPath>(fNowClipShape);
        relative_clip.offset(-fNowFrame.left(), -fNowFrame.top());
        shape = relative_clip;
    } else {
        return;
    }
    if (fRelativeClipShape != shape) {
        fRelativeClipShape = shape;
        fClipNeedUpdate = true;
    }
}

void OHRenderNode::updateRelativeMatrix() {
    SkMatrix matrixMove;
    SkMatrix matrix = fFatherMatrix;
    auto translateX = -fNowFrame.x();
    auto translateY = -fNowFrame.y();
    if (std::abs(translateX) > 1e-5f || std::abs(translateY) > 1e-5f) {
        matrixMove.setTranslate(translateX, translateY);
        matrix.postConcat(matrixMove);
        matrixMove.setTranslate(-translateX, -translateY);
        matrix.preConcat(matrixMove);
    }

    if (!fMatrix.approximatelyEquals(matrix)) {
        fMatrix = matrix;
        fMatrixNeedUpdate = true;
    }
}

void OHRenderNode::updateDrawingOffset() {
    auto x = -fNowFrame.x();
    auto y = -fNowFrame.y();
    // offset => px
    if (!FLOAT_IS_EQUAL(fOffsetX, x, 0.1f)  || !FLOAT_IS_EQUAL(fOffsetY, y, 0.1f) ) {
        fOffsetX = x;
        fOffsetY = y;
        fNodeNeedRedraw = true;
    }
}

void OHRenderNode::updateNowFrame() {
    auto suit_frame = getSuitFrameSize();
    if (!fNowFrame.approximatelyEquals(suit_frame, 1e-1)) {
        char traceStr[300];
        snprintf(traceStr, 299, "updateNowFrame-position-[%.0f,%.0f=>%.0f,%.0f]-size-[%.0f,%.0f=>%.0f,%.0f]",
                 fNowFrame.x(), fNowFrame.y(), suit_frame.x(), suit_frame.y(), fNowFrame.width(), fNowFrame.height(),
                 suit_frame.width(), suit_frame.height());
        TRACE_EVENT0("skia", TRACE_STR_COPY(traceStr));
        // When the original node size is zero, updating the size requires forced invalidation; otherwise, ArkUI will
        // not repaint the node.
        if (fNowFrame.isEmpty()) {
            fNodeNeedRedraw = true;
        }
        fNowFrame = suit_frame;
        fNowFrameHasChanged = true;
        // read texture + read framebuffer + write
        fNodeDrawCost = (1 + 1 + fScaleRatio) * fNowFrame.width() * fNowFrame.height();
        updateClipAreaWithFrameChange();
    }
}

void OHRenderNode::setRealFrame(SkRect &realFrame, bool noLimit) {
    if (!fSizeFixed) {
        fNoLimitSize = noLimit;
        // Expand the node bounds as needed, without shrinking, to reduce costly rebuilds, 
        // measurements, and redraws caused by frequent size adjustments.
        if (fRealFrame.isEmpty() || !fRealFrame.approximatelyEquals(realFrame, 1e-3)) {
            fRealFrame = realFrame;
            fRealFrameHasChanged = true;
        }
    }
}

void OHRenderNode::setFixedFrame(SkRect &fixedFrame) {
    fSizeFixed = true;
    fNoLimitSize = false;
    fMaxDeviceFrame = fixedFrame;
    if (fRealFrame.isEmpty() || !fRealFrame.approximatelyEquals(fixedFrame, 1e-3)) {
        fRealFrame = fixedFrame;
        fRealFrameHasChanged = true;
    }
}

void OHRenderNode::updateNodeStatus() {
    if (!fFatherMatrixHasChanged && !fClipShapeHasChanged && !fRealFrameHasChanged && !fFatherFrameHasChanged &&
        !fContentHasChanged) {
        return;
    }
    if (fDrawInPicture && !fNeedMoveToInvisibleArea) {
        return;
    }
    TRACE_EVENT1("skia", __FUNCTION__, "id", fNodeId);
    
    // use RealFrame to update NowFrame
    if (fRealFrameHasChanged || (fNoLimitSize && (fFatherMatrixHasChanged || fFatherFrameHasChanged))) {
        updateNowFrame();
    }
    
    if (fNowFrameHasChanged || fFatherFrameHasChanged) {
        // fFatherFrame/fNowFrame => relative location
        updateRelativeLocation();
    }
    if (fNowFrameHasChanged || fClipShapeHasChanged) {
        updateRelativeClipArea();
    }
    if (fFatherMatrixHasChanged || fNowFrameHasChanged) {
        updateRelativeMatrix();
    }
    if (fNowFrameHasChanged) {
        updateDrawingOffset();
    }
    pushStatusToModify();
    doRedraw();

    if (fContentHasChanged) {
        fContentUnchangedFrame = 0;
    } else {
        fContentUnchangedFrame++;
    }


    if (fContentHasChanged || fNowFrameHasChanged) {
        for (auto& node: fChildList) {
            node->updateFatherNodeFrame(fNowFrame);
            node->updateNodeStatus();
        }
    }
    fFatherMatrixHasChanged= false;
    fClipShapeHasChanged = false;
    fNowFrameHasChanged = false;
    fFatherFrameHasChanged = false;
    fContentHasChanged = false;
    fNeedMoveToInvisibleArea = false;
}

void OHRenderNode::pushStatusToModify() {
    if (!fSizeNeedUpdate && !fPositionNeedUpdate && !fMatrixNeedUpdate && !fClipNeedUpdate) {
        return;
    }
    auto env = OHEnv::GetInstance()->GetEnv();
    napi_value localObject;
    napi_status status = napi_get_reference_value(env, fJsObject, &localObject);
    if (fSizeNeedUpdate) {
        SkVector v = {(float)(fNowFrameSize.width() / gVP2PXRatio), (float)(fNowFrameSize.height() / gVP2PXRatio)};
        NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::NodeOp::SIZE, localObject, v);
        fSizeNeedUpdate = false;
    }
    if (fPositionNeedUpdate) {
        SkVector v = {(float)(fRelativeOffset.x() / gVP2PXRatio), (float)(fRelativeOffset.y() / gVP2PXRatio)};
        NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::NodeOp::POSITION, localObject, v);
        fPositionNeedUpdate = false;
    }
    if (fMatrixNeedUpdate) {
        NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::NodeOp::TRANSFORM, localObject, fMatrix);
        fMatrixNeedUpdate = false;
    }
    if (fClipNeedUpdate) {
        if (std::holds_alternative<SkRect>(fRelativeClipShape)) {
            NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::NodeOp::SHAPE_CLIP, localObject, std::get<SkRect>(fRelativeClipShape));
        } else if (std::holds_alternative<SkRRect>(fRelativeClipShape)) {
            NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::NodeOp::SHAPE_CLIP_RRECT, localObject, std::get<SkRRect>(fRelativeClipShape));
        } else if (std::holds_alternative<SkPath>(fRelativeClipShape)) {
            NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::NodeOp::SHAPE_CLIP_PATH, localObject, std::get<SkPath>(fRelativeClipShape)); 
        } else {
            assert(0);
        }
        fClipNeedUpdate = false;
    }
//    TRACE_EVENT0("skia", __FUNCTION__);
//    
////    SkDebugf("update node frame %f %f %f %f", fNowFrame.x(), fNowFrame.y(), fNowFrame.width(), fNowFrame.height());
//    if (fSizeHasChanged) {
//        TRACE_EVENT0("skia", "changeNodeSize");
//        napi_value frame;
//        napi_get_named_property(env, localObject, "size", &frame);
//        napi_value napi_width, napi_height;
//        napi_create_double(env, fNowFrameSize.width() / gVP2PXRatio, &napi_width);
//        napi_set_named_property(env, frame, "width", napi_width);
//        napi_create_double(env, fNowFrameSize.height() / gVP2PXRatio, &napi_height);
//        napi_set_named_property(env, frame, "height", napi_height);
//        status = napi_set_named_property(env, localObject, "size", frame);
//        if (status != napi_ok) {
//            return;
//        }
//        fSizeHasChanged = false;
//    }
//    if (fPositionHasChanged) {
//        TRACE_EVENT0("skia", "changeNodePosition");
//        napi_value frame;
//        napi_get_named_property(env, localObject, "position", &frame);
//        napi_value napi_x, napi_y;
//        napi_create_double(env, fRelativeOffset.x() / gVP2PXRatio, &napi_x);
//        napi_set_named_property(env, frame, "x", napi_x);
//        napi_create_double(env, fRelativeOffset.y() / gVP2PXRatio, &napi_y);
//        napi_set_named_property(env, frame, "y", napi_y);
//        status = napi_set_named_property(env, localObject, "position", frame);
//        if (status != napi_ok) {
//            return;
//        }
//        fPositionHasChanged = false;
//    }
//    if (fMatrixHasChanged) {
//        TRACE_EVENT0("skia", "changeNodeMatrix");
//        napi_value arrayBuffer = nullptr;
//        void* data = nullptr;
//    
//        size_t length = 16 * sizeof(float);
//        napi_create_arraybuffer(env, length, &data, &arrayBuffer);
//        SkM44 m4(fMatrix);
//        float* floatData = static_cast<float*>(data);
//        m4.getColMajor(floatData);
//        napi_value float32Array = nullptr;
//        napi_create_typedarray(env, napi_float32_array, 16, arrayBuffer, 0, &float32Array);
//        napi_set_named_property(env, localObject, "transform", float32Array);
//        fMatrixHasChanged = false;
//    }
//    if (fClipHasChanged) {
////        TRACE_EVENT0("skia", "changeNodeClip");
////        napi_value shape;
////        napi_get_named_property(env, localObject, "shapeClip", &shape);
////        napi_value d;
////        napi_value rect;
////        napi_create_object(env, &rect);
////        napi_create_double(env, fNowClipRect.left() - fNowFrame.left(), &d);
////        napi_set_named_property(env, rect, "left", d);
////        napi_create_double(env, fNowClipRect.right() - fNowFrame.left(), &d);
////        napi_set_named_property(env, rect, "right", d);
////        napi_create_double(env, fNowClipRect.top() - fNowFrame.top(), &d);
////        napi_set_named_property(env, rect, "top", d);
////        napi_create_double(env, fNowClipRect.bottom() - fNowFrame.top(), &d);
////        napi_set_named_property(env, rect, "bottom", d);
////        status = napi_set_named_property(env, shape, "setRectShape", rect);
////        SkDebugf("clip set %f %f %f %f %d", fNowClipRect.left(), fNowClipRect.top(), fNowClipRect.width(), fNowClipRect.height(), status);
////        status = napi_set_named_property(env, localObject, "shapeClip", shape);
////        SkDebugf("clip set  %d", status);
////
////        if (status != napi_ok) {
////            return;
////        }
//        fClipHasChanged = false;
//    }
}

SkRect OHRenderNode::getJSNodeSize() {
    auto env = OHEnv::GetInstance()->GetEnv();
    napi_value frame;
    napi_value localObject;
    napi_status status = napi_get_reference_value(env, fJsObject, &localObject);
    if (status != napi_ok) {
        return {};
    }
    napi_get_named_property(env, localObject, "frame", &frame);

    napi_value napi_x, napi_y, napi_width, napi_height;
    napi_get_named_property(env, frame, "x", &napi_x);
    napi_get_named_property(env, frame, "y", &napi_y);
    napi_get_named_property(env, frame, "width", &napi_width);
    napi_get_named_property(env, frame, "height", &napi_height);
    double x = 0, y = 0, w = 0, h = 0;
    napi_get_value_double(env, napi_x, &x);
    napi_get_value_double(env, napi_y, &y);
    napi_get_value_double(env, napi_width, &w);
    napi_get_value_double(env, napi_height, &h);
    return SkRect::MakeXYWH(x * gVP2PXRatio, y * gVP2PXRatio, w * gVP2PXRatio, h * gVP2PXRatio);
}

SkRect OHRenderNode::getFrameSize() {
    return fNowFrame;
}


void OHRenderNode::clearChildren() {
    fChildList.clear();
    fChildMap.clear();
    fSubRecorders.clear();
//    invokeJsFunction("clearChildren", 0, nullptr, nullptr);
    NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::CLEAR_CHILDREN, getLocalJsObject(), nullptr, nullptr);
}

void OHRenderNode::restartChildAdd() {
    char trace_str[50];
    snprintf(trace_str, 49, "Recording-id-%d", fNodeId);
    TRACE_EVENT_BEGIN0("skia", TRACE_STR_COPY(trace_str));
    fChildPaintArea = SkRect::MakeEmpty();
    fNeedReorder = false;
    fChildOrderList.clear();
    fChildAppend.clear();
    fNeedDelayChildAppend = false;
    fPictureChildNum = 0;
//    fStartChildIt = fChildList.begin();
    fSubRecorderNextIt = fSubRecorders.begin();
}

void OHRenderNode::finishChildAdd() {
    TRACE_EVENT_END();
    if (fSubRecorderNextIt != fSubRecorders.end()) {
        fSubRecorders.erase(fSubRecorderNextIt, fSubRecorders.end());
        fSubRecorderNextIt = fSubRecorders.end();
    }
    auto self_node = getLocalJsObject();
    
    if (fChildList.size() == fChildOrderList.size()) {
        // if the order list is same with the origin list, we needn't do reorder.
        auto it1 = fChildList.begin();
        auto it2 = fChildOrderList.begin();
        while(it1 != fChildList.end()) {
            if (it1 == *it2) {
                it1++;
                it2++;
            } else {
                break;
            }
        }
        if (it1 == fChildList.end()) {
            fNeedReorder = false;
        }
    }

    // needReorder => remove all and append each node
    // fChildList.size() > fChildOrderList.size() * 2 => invisible nodes are two much => need clean them
    if (fNeedReorder || fChildList.size() > fChildOrderList.size() * 2) {
        // @todo 节点操作次数还能进一步减少，后续需观察是否有必要
        TRACE_EVENT1("skia", __FUNCTION__, "reorder", fChildOrderList.size());
        NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::CLEAR_CHILDREN, self_node, nullptr, nullptr);
        for (auto &it : fChildOrderList) {
            fChildList.splice(fChildList.end(), fChildList, it);
            NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::APPEND_CHILD, self_node,
                                                    it->get()->getLocalJsObject(), nullptr);
        }
        for (auto it = fChildList.begin(); it != *fChildOrderList.begin() && it != fChildList.end();) {
            fChildMap.erase(it->get());
            (*it)->setParent(nullptr);
            it = fChildList.erase(it);
        }
    } else {
        TRACE_EVENT1("skia", __FUNCTION__, "append", fChildAppend.size());
        for (auto &it : fChildAppend) {
            fChildList.splice(fChildList.end(), fChildList, it);
            NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::APPEND_CHILD, self_node,
                                                    it->get()->getLocalJsObject(), nullptr);
        }
        for (auto &node : fChildList) {
            if (!node->fVisible) {
                node->moveToInvisibleArea();
//                auto area = node->getDrawArea();
//                SkDebugf("paintArea cannot see node: %f %f %f %f ( %f %f)", area.left(), area.top(),
//                                area.right(), area.bottom(), area.width(), area.height());
            }
        }
    }
    return;
}

void OHRenderNode::appendChild(std::shared_ptr<OHRenderNode> childNode) {
    if (!childNode) {
        return;
    }
//    TRACE_EVENT0("skia", __FUNCTION__ );
    assert(!childNode->fVisible || childNode->fSizeFixed);
    childNode->fVisible = true;
    childNode->fDrawInPicture = false;

    auto paintArea = childNode->getPaintArea();
    // SkDebugf("paintArea %f %f %f %f size:%f %f", paintArea.left(), paintArea.top(), paintArea.right(), paintArea.bottom(), paintArea.width(), paintArea.height());
    auto childIt = fChildMap.find(childNode.get());

    if (childIt != fChildMap.end()) {
        SkRect temp;
        if (!paintArea.has_value() || !fChildPaintArea.has_value() ||
            temp.intersect(paintArea.value(), fChildPaintArea.value()) && temp.width() > 0.1f && temp.height() > 0.1f) {
            fNeedReorder = true;
        }
        fChildOrderList.push_back(childIt->second);

    } else {
        auto it = fChildList.insert(fChildList.end(), childNode);
        fChildMap[childNode.get()] = it;
        fChildOrderList.push_back(it);
        fChildAppend.push_back(it);
        
        childNode->markActive(true);
        fNeedDelayChildAppend = true;
    }
    
    if (gEnableRasterCache) {
        // here to decide to mark group
        childNode->updateScaleRatio(fScaleRatio);
        childNode->rasterCacheStatusUpdate();
        if (fScaleRatio > 0.0001f) {
            childNode->fMaxDeviceFrame =
                SkRect::MakeWH(fMaxDeviceFrame.width() * childNode->fScaleRatio / fScaleRatio,
                               fMaxDeviceFrame.height() * childNode->fScaleRatio / fScaleRatio);
        }
    }
    
    if (paintArea.has_value() && fChildPaintArea.has_value()) {
        fChildPaintArea.value().join(paintArea.value());
    } else {
        fChildPaintArea = std::nullopt;
    }
    return;
}

void OHRenderNode::removeChild(OHRenderNode *childNode) {
    if (!childNode) {
        return;
    }
    napi_value self_node = getLocalJsObject();
    auto it = fChildMap.find(childNode);
    if (it != fChildMap.end()) {
        auto childJsObject = childNode->getLocalJsObject();
//        invokeJsFunction("removeChild", 1, &childJsObject, nullptr);
        NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::REMOVE_CHILD, self_node, childJsObject, nullptr);

        fChildList.erase(it->second);
        fChildMap.erase(it);
    }
}

OHRenderNode *OHRenderNode::getParent() {
    return fParent;
}

void OHRenderNode::setParent(OHRenderNode *node) {
    /**
     * When a node is reused, it might still be held by another node.
     * Due to the delayed child node deletion mechanism, another node won't remove this child node.
     * This could cause this node to become a child of two different parent nodes,
     * leading to rendering anomalies.
     *
     * Therefore, when a parent changes, the old parent-child relationship must be cleared.
     * To minimize redundant remove/add operations, calls to setParent() are restricted -
     * it should only be invoked when the parent actually changes.
     */
    if (fParent != node && fParent != nullptr && node != nullptr) {
        // No remove operation is required when updating parent to null, as this indicates the parent node is about to
        // be fully destroyed.
        assert(!fVisible || fSizeFixed);
        NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::REMOVE_CHILD, fParent->getLocalJsObject(),
                                                getLocalJsObject(), nullptr);
        auto it = fParent->fChildMap.find(this);
        if (it != fParent->fChildMap.end()) {
            fParent->fChildList.erase(it->second);
            fParent->fChildMap.erase(it);
        }
    }
    fParent = node;
}

std::shared_ptr<SkPictureRecorder> OHRenderNode::getOrCreateNextSubRecorder() {
    if (fSubRecorderNextIt == fSubRecorders.end()) {
        fSubRecorderNextIt = fSubRecorders.insert(fSubRecorderNextIt, std::make_shared<SkPictureRecorder>());
    }
    auto retIt = fSubRecorderNextIt;
    ++fSubRecorderNextIt;
    return *retIt;
}

void OHRenderNode::updatePicture(SkPicture *picture) {
    fNodePicture = picture;
    if (picture) {
        if (fNodeContentHash != picture->hash()) {
            fNodeNeedRedraw = true;
        }
        fContentHasChanged = true;
        fNodeRedrawCnt = 0;
        fPictureDrawCost = picture->getDrawCostEstimate();
        fPictureCmd = *fNodePicture;
        fPictureTreeCmdCache = nullptr;
        fNodeContentHash = picture->hash();
    } else {
        // updatePicture with null means SkPicture is destroying.
        for (auto& node: fChildList) {
            auto picture = node->getPicture();
            if (picture && node->fVisible) {
                picture->recycleNode(node);
            }
            node->fVisible = false;
        }
    }
}

SkPicture *OHRenderNode::getPicture() {
    return fNodePicture;
}

void OHRenderNode::moveToInvisibleArea() {
    SkRect area;
    fFatherMatrix.mapRect(&area, fNowFrame);
    SkRect temp;
    // father area intersect this node.
    if (temp.intersect(fFatherFrame, area)) {
        SkMatrix offscreenMatrix = fFatherMatrix;
        SkVector offset = {-abs(fFatherFrame.left() - area.right()) - NODE_SIZE_ALIGNMENT * 10,
                           -abs(fFatherFrame.top() - area.bottom()) - NODE_SIZE_ALIGNMENT * 10};
        // use postTranslate to ensure do translate first (no scale effect).
        offscreenMatrix.postTranslate(offset.x(), offset.y());
        updateFatherMatrix(offscreenMatrix);
        fNeedMoveToInvisibleArea = true;
    }
}

//SkRect OHRenderNode::getDrawArea() {
//    SkRect area = fNowFrame;
//    if (!std::holds_alternative<std::monostate>(fNowClipShape)) {
//        area.intersect(fNowClipRect);
//    }
//    SkRect retArea;
//    fFatherMatrix.mapRect(&retArea, area);
//    retArea.intersect(fFatherFrame);
//    return retArea;
//}

void OHRenderNode::nodeDraw(OH_Drawing_Canvas *oh_canvas) {
    TRACE_EVENT2("skia", __FUNCTION__, "id", fNodeId, "hash", fNodeContentHash);
//    auto frame_size = getJSNodeSize();
//    int width = OH_Drawing_CanvasGetWidth(oh_canvas);
//    int height = OH_Drawing_CanvasGetHeight(oh_canvas);
//    OH_Drawing_CanvasClipRect(oh_canvas, (OH_Drawing_Rect *)&frame_size, OH_Drawing_CanvasClipOp::INTERSECT, false);
//    SkDebugf("node clip %f %f %f %f real  %d %d", frame_size.x(), frame_size.y(), frame_size.width(), frame_size.height(), width, height);

//    // used for debug
//    char trace_str[100];
//    snprintf(trace_str, 99, "frame-loc-%.0f,%.0f-size-%.0f,%.0f", fNowFrame.x(), fNowFrame.y(), fNowFrame.width(),
//             fNowFrame.height());
//    TRACE_EVENT0("skia", TRACE_STR_COPY(trace_str));
    if (fPictureCmd) {
        OH_Drawing_CanvasTranslate(oh_canvas, fOffsetX, fOffsetY);
        OHDrawingAPI::OH_Drawing_CanvasDrawRecordCmdNesting(oh_canvas, fPictureCmd.get());
        fNodeNeedRedraw = false;
    }
}

uint64_t OHRenderNode::pictureDraw(OH_Drawing_Canvas *canvas, bool needCache) {
    if (fPictureCmd) {
        TRACE_EVENT1("skia", "playback-in-picture", "id", fNodeId);
        if (needCache && fChildList.size() > 0 && !fPictureTreeCmdCache) {
            generatePictureTreeCmdCache();
        }
        if (needCache) {
            // needCache => root picture node
            // child node's fDrawInPicture will not be reset to false if drawn in node, so we don't set it to true.
            fDrawInPicture = true;
        }
        if (fPictureTreeCmdCache) {
            OHDrawingAPI::OH_Drawing_CanvasDrawRecordCmdNesting(canvas, fPictureTreeCmdCache.get());
            return fPictureTreeHash;
        } else {
            DrawingHash hasher(fNodeContentHash);
            OHDrawingAPI::OH_Drawing_CanvasDrawRecordCmdNesting(canvas, fPictureCmd.get());
            for (auto &node : fChildList) {
                if (node->fVisible) {
                    OH_Drawing_CanvasSave(canvas);
                    OH_Drawing_CanvasConcatMatrix(canvas, node->fFatherMatrix);
                    if (node->fNeedClip) {
                        if (std::holds_alternative<SkRect>(node->fNowClipShape)) {
                            const SkRect &clipNow = std::get<SkRect>(node->fNowClipShape);
                            OH_Drawing_CanvasClipRect(canvas, (OH_Drawing_Rect *)&clipNow,
                                                      OH_Drawing_CanvasClipOp::INTERSECT, false);
                        } else if (std::holds_alternative<SkRRect>(node->fNowClipShape)) {
                            const SkRRect &clipNow = std::get<SkRRect>(node->fNowClipShape);
                            OH_Drawing_RoundRect *oh_rrect = clipNow.buildOHRRect();
                            OH_Drawing_CanvasClipRoundRect(canvas, oh_rrect, OH_Drawing_CanvasClipOp::INTERSECT, false);
                            OH_Drawing_RoundRectDestroy(oh_rrect);
                        } else if (std::holds_alternative<SkPath>(node->fNowClipShape)) {
                            const SkPath &clipNow = std::get<SkPath>(node->fNowClipShape);
                            OH_Drawing_CanvasClipPath(canvas, clipNow, OH_Drawing_CanvasClipOp::INTERSECT, false);
                        }
                    }
                    auto nodeHash = node->pictureDraw(canvas);
                    if (nodeHash) {
                        hasher.push(node->fFatherMatrix.hash());
                        hasher.push(GetClipShapeHash(node->fDeviceClipShape));
                        hasher.push(GetClipShapeHash(node->fPendingDiffShape));
                        hasher.push(nodeHash);
                    }
                    OH_Drawing_CanvasRestore(canvas);
                }
            }
            return hasher.digest();
        }
    }
    return 0;
}

void OHRenderNode::doRedraw() {
    if (fCallback) {
        TRACE_EVENT0("skia", "doRedraw with callback");
        // this is the root node.
        if (fRecorder == nullptr) {
            fRecorder = new SkPictureRecorder;
            auto root_node = fRecorder->getCachedRenderNode();
            if (root_node) {
                NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::APPEND_CHILD, getLocalJsObject(),
                                                        root_node->getLocalJsObject(), nullptr);
            }
        }
        fRecorderPicture.reset();
        auto root_frame = getJSNodeSize();
        auto root_real_size = SkRect::MakeWH(root_frame.width(), root_frame.height());
        SkCanvas *sk_canvas = fRecorder->beginRecording(root_real_size);
        if (sk_canvas && sk_canvas->getRenderNode()) {
            auto render_node = sk_canvas->getRenderNode();
            render_node->setFixedFrame(root_real_size);

            fCallback(fCallbackContext, sk_canvas);
            fRecorderPicture = fRecorder->finishRecordingAsPicture();

            render_node->setParent(this);
            
            TRACE_EVENT0("skia", "updateNodeStatus");
            render_node->updateNodeStatus();
        }
        NodeStatusModify::GetInstance()->doModify();
    }
    if (fNodeNeedRedraw || fMarkInvalidate) {
//        invokeJsFunction("invalidate", 0, nullptr, nullptr, nullptr);
        NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::INVALIDATE, getLocalJsObject(), nullptr, nullptr);
        fMarkInvalidate = false;
    }
}

napi_value OHRenderNode::getLocalJsObject() {
    auto env = OHEnv::GetInstance()->GetEnv();

    napi_value localObject;
    napi_status status = napi_get_reference_value(env, fJsObject, &localObject);
    if (status != napi_ok) {
        return nullptr;
    }
    return localObject;
}

napi_status OHRenderNode::invokeJsFunction(const char *name, size_t argc, const napi_value* argv, napi_value *result, napi_value node) {
    TRACE_EVENT0("skia", TRACE_STR_COPY(name));

    auto env = OHEnv::GetInstance()->GetEnv();
    napi_value localObject = node;
    if (localObject == nullptr) {
        localObject = getLocalJsObject();
    }
    
    napi_value fun;
    napi_status status = napi_get_named_property(env, localObject, name, &fun);
    if (status != napi_ok) {
        return status;
    }
    
    status = napi_call_function(env, localObject, fun, argc, argv, result);
    if (status != napi_ok) {
        SkDebugf("error invoke status %d!", status);
        return status;
    }
    return napi_ok;
}


void OHRenderNode::updateScaleRatio(float fatherScaleRatio) {
    if (!gEnableRasterCache) {
        return;
    }
    
    if (!fFatherMatrixHasChanged && fFatherScaleRatio == fatherScaleRatio) {
        return;
    }
    
    SkVector vx = fFatherMatrix.mapVector(1, 0);
    SkVector vy = fFatherMatrix.mapVector(0, 1);
    
    float lenX = vx.length();
    float lenY = vy.length();
    
    constexpr float epsilon = 1e-3f;
    
    bool isOrthogonal = std::abs(vx.dot(vy)) < epsilon;
    bool isUniform = (lenY > epsilon && std::abs(lenX / lenY - 1.0f) < 0.1f);
    
    if (isOrthogonal && isUniform) {
        fScaleRatio = fatherScaleRatio * lenX * lenY;
    } else {
        fScaleRatio = 0.f;
    }
    fFatherScaleRatio = fatherScaleRatio;
    
}
    
void OHRenderNode::rasterCacheStatusUpdate() {
    if (!gEnableRasterCache) {
        return;
    }
    fNodeRedrawCnt++;
    // +0.05 
    if (fForbidRasterCache || abs(fScaleRatio - 1.0f) > 0.15f || fNodeRedrawCnt < 5 || fPictureDrawCost < fNodeDrawCost) {
        // cannot create raster cache
        if (fIsInRasterCache) {
            // disable raster cache.
            fIsInRasterCache = false;
            fRasterCacheStateHasChange = true;
        }
        if (fReadyToCreateRasterCache) {
            fReadyToCreateRasterCache = false;
            fCacheReadyFrameCnt = 0;
        }
    }
    char traceStr[300];
    snprintf(traceStr, 299, "rasterCacheStatusUpdate-cost-%.0f,%.0f-playback-%zu", fPictureDrawCost, fNodeDrawCost,
             fNodeRedrawCnt);
    TRACE_EVENT0("skia", TRACE_STR_COPY(traceStr));
    if (abs(fScaleRatio - 1.0f) < 0.1 && !fIsInRasterCache && fPictureDrawCost > fNodeDrawCost * 1.2) {
        if (fNodeRedrawCnt > 5) {
            // can be drawn in raster cache
            fReadyToCreateRasterCache = true;
            fCacheReadyFrameCnt++;
        }
        if (fCacheReadyFrameCnt > 5 && fReadyToCreateRasterCache) {
            fIsInRasterCache = true;
            fRasterCacheStateHasChange = true;
            fReadyToCreateRasterCache = false;
            fCacheReadyFrameCnt = 0;
        }
    }
    
    if (fRasterCacheStateHasChange) {
        TRACE_EVENT1("skia", "rasterCacheStatusUpdate", "enable", (int)fIsInRasterCache);
        if (fIsInRasterCache) {
            NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::MARK_GROUP, getLocalJsObject(), nullptr, nullptr);
        } else {
            NodeStatusModify::GetInstance()->pushTo(NodeStatusModify::MARK_NO_GROUP, getLocalJsObject(), nullptr, nullptr);
        }
        fRasterCacheStateHasChange = false;
    }
}

void OHRenderNode::generatePictureTreeCmdCache() {
    // generate recordCmd cache
    auto recorder = OH_Drawing_RecordCmdUtilsCreate();
    OH_Drawing_Canvas *recordCanvas = nullptr;
    OH_Drawing_RecordCmdUtilsBeginRecording(recorder, fRealFrame.width(), fRealFrame.height(), &recordCanvas);
    if (recordCanvas) {
        fPictureTreeHash = pictureDraw(recordCanvas, false);
        OH_Drawing_RecordCmd *recordCmd = nullptr;
        OH_Drawing_RecordCmdUtilsFinishRecording(recorder, &recordCmd);
        if (recordCmd) {
            fPictureTreeCmdCache =
                OHDrawingRecordCmdSPtr(recordCmd, [](OH_Drawing_RecordCmd *cmd) { OH_Drawing_RecordCmdDestroy(cmd); });
        }
    }
    OH_Drawing_RecordCmdUtilsDestroy(recorder);
}

float OHRenderNode::getDrawCost() {
    // @todo add clip
    if (fIsInRasterCache) {
        return fNodeDrawCost;
    } else {
        return fPictureDrawCost;
    }
}

napi_value OHRenderNode::CreateJsLiveNode(OHRenderNode **node) {
    auto ptr = new OHRenderNode();
    auto jsNode = ptr->setLiveWithJs();
    if (!ptr->fStatusOK || jsNode == nullptr) {
        delete ptr;
        return nullptr;
    }
    *node = ptr;
    return jsNode;
}

std::shared_ptr<OHRenderNode> OHRenderNode::CreateNormalNode() {
    auto env = OHEnv::GetInstance()->GetEnv();
    
    napi_handle_scope scope;
    napi_open_handle_scope(env, &scope);
    auto ret = std::make_shared<OHRenderNode>();
    napi_close_handle_scope(env, scope);
    
    if (!ret->fStatusOK) {
        return nullptr;
    }
    return ret;
}

std::shared_ptr<OHRenderNode> OHRenderNode::CreateExternalNode(napi_value node) {
    auto env = OHEnv::GetInstance()->GetEnv();
    
    napi_handle_scope scope;
    napi_open_handle_scope(env, &scope);
    auto ret = std::make_shared<OHRenderNode>(node);
    napi_close_handle_scope(env, scope);
    if (!ret->fStatusOK) {
        return nullptr;
    }
    return ret;
}

void OHRenderNode::RenderNodeDraw(napi_env env, napi_value jsCanvas, napi_value jsNode) {
    void *ptr = nullptr;
    OHRenderNode* node = nullptr;
    
    napi_value napi_ptr;
    napi_status status = napi_get_named_property(env, jsNode, "OHRenderNodePtr", &napi_ptr);
    status = napi_get_value_external(env, napi_ptr, (void **)&ptr);
    node = (OHRenderNode *)ptr;

    napi_unwrap(env, jsCanvas, &ptr);
    OH_Drawing_Canvas *oh_canvas = reinterpret_cast<OH_Drawing_Canvas *>(ptr);

    if (node) {
        node->nodeDraw(oh_canvas);
    }
    return;
}

void OHRenderNode::RenderNodeNotifyRedraw(napi_env env, napi_value jsNode) {
    void *ptr = nullptr;
    OHRenderNode* node = nullptr;
    
    napi_value napi_ptr;
    napi_status status = napi_get_named_property(env, jsNode, "OHRenderNodePtr", &napi_ptr);
    status = napi_get_value_external(env, napi_ptr, (void **)&ptr);
    node = (OHRenderNode *)ptr;

    if (node && node->fAsyncTask != nullptr) {
        uint result = 0;
        // ensure the task can be done (keep node alive).
        napi_reference_ref(env, node->fJsObject, &result);
        uv_async_send(node->fAsyncTask);
    }
    return;
}

void OHRenderNode::RenderNodeDoRedraw(uv_async_t *handle) {
    OHRenderNode *node = (OHRenderNode *)handle->data;
    if (node) {
        auto env = OHEnv::GetInstance()->GetEnv();
        napi_handle_scope scope;
        napi_open_handle_scope(env, &scope);

        {
            // Used to trigger RequestNextVSync and must not be removed.
            napi_value localObject;
            napi_status status = napi_get_reference_value(env, node->fJsObject, &localObject);
            if (status == napi_ok) {
                napi_value napi_true;
                napi_get_boolean(env, true, &napi_true);
                napi_set_named_property(env, localObject, "clipToFrame", napi_true);
            }
        }

        node->doRedraw();
        uint result = 0;
        napi_reference_unref(env, node->fJsObject, &result);
        napi_close_handle_scope(env, scope);
    }
}

void OHRenderNode::SetNodeConstructor(napi_env env, napi_value fun) {
    OHEnv::GetInstance()->SetEnv(env);
    if (OHRenderNode::gNodeConstructorRef != NULL) {
        napi_delete_reference(env, OHRenderNode::gNodeConstructorRef);
    }
    napi_create_reference(env, fun, 1, &OHRenderNode::gNodeConstructorRef);
    return;
}

napi_value OHRenderNode::CreateRootRenderNode(void *context, NodeDrawCallback callback) {
    OHRenderNode *node = nullptr;
    auto jsNode = OHRenderNode::CreateJsLiveNode(&node);
    if (jsNode != nullptr && node != nullptr) {
        node->registerCallback(context, callback);
        return jsNode;
    } else {
        return nullptr;
    }
}

void OHRenderNode::SetPixelRatio(double ratio) {
    OHRenderNode::gVP2PXRatio = ratio;
}


void NodeStatusModify::BuildInstance(napi_env env, napi_value fun) {
    if (GetInstance()->fJsObject != nullptr) {
        return;
    }
    napi_value instance = nullptr;
    napi_status status = napi_new_instance(env, fun, 0, nullptr, &instance);
    if (status != napi_ok) {
        return;
    }
    status = napi_create_reference(env, instance, 1, &GetInstance()->fJsObject);
    if (status != napi_ok) {
        return;
    }
}

void NodeStatusModify::doModify() {
    TRACE_EVENT2("skia", "modifyNodeJsStatus", "op", fOps.size(), "nodes", fNodes.size());
    
    if (fJsObject == nullptr) {
        return;
    }
    auto env = OHEnv::GetInstance()->GetEnv();
    napi_value localObject;
    napi_status status = napi_get_reference_value(env, fJsObject, &localObject);
    if (status != napi_ok) {
        SkDebugf("get Modify failed");
        return;
    }
    if (fOps.size() == 0) {
        return;
    }

    size_t length = fOps.size() * sizeof(int) + fNumbers.size() * sizeof(float);
    napi_value arrayBuffer;
    void *data = nullptr;
    napi_create_arraybuffer(env, length, &data, &arrayBuffer);
    memcpy(data, fOps.data(), fOps.size() * sizeof(int));
    memcpy((char *)data + fOps.size() * sizeof(int), fNumbers.data(), fNumbers.size() * sizeof(float));

    napi_value int32Array;
    napi_create_typedarray(env, napi_int32_array, fOps.size(), arrayBuffer, 0, &int32Array);

    napi_value float32Array;
    napi_create_typedarray(env, napi_float32_array, fNumbers.size(), arrayBuffer, fOps.size() * sizeof(int), &float32Array);

    napi_value nodeArray;
    napi_create_array_with_length(env, fNodes.size(), &nodeArray);
    for (int i = 0; i < fNodes.size(); i++) {
        napi_set_element(env, nodeArray, i, fNodes[i]);
    }

    napi_value svgStrArray;
    napi_create_array_with_length(env, fStrs.size(), &svgStrArray);
    for (size_t i = 0; i < fStrs.size(); ++i) {
        napi_value strValue;
        napi_create_string_utf8(env, fStrs[i].c_str(), fStrs[i].size(), &strValue);
        napi_set_element(env, svgStrArray, i, strValue);
    }

    napi_set_named_property(env, localObject, "opArray", int32Array);
    napi_set_named_property(env, localObject, "numberArray", float32Array); 
    napi_set_named_property(env, localObject, "nodeArray", nodeArray);
    napi_set_named_property(env, localObject, "svgStrArray", svgStrArray);

    napi_value fun;
    status = napi_get_named_property(env, localObject, "doModify", &fun);
    if (status != napi_ok) {
        SkDebugf("cannot get js doModify function! status %d", status);
        return;
    }

    TRACE_EVENT0("skia", "callJS");
    status = napi_call_function(env, localObject, fun, 0, nullptr, nullptr);
    if (status != napi_ok) {
        SkDebugf("error invoke doModify ret status %d!", status);
        return;
    }
    fNodes.clear();
    fOps.clear();
    fNumbers.clear();
    fStrs.clear();
    return;
    
}

void NodeStatusModify::pushTo(NodeOp op, napi_value node1, napi_value node2, napi_value node3) {
    fOps.push_back(op);
    fNodes.push_back(node1);
    if (op == NodeOp::REMOVE_CHILD) {
        fNodes.push_back(node2);
    } else if (op == NodeOp::INSERT_CHILD_AFTER) {
        fNodes.push_back(node2);
        fNodes.push_back(node3);
    } else if (op == NodeOp::APPEND_CHILD) {
        fNodes.push_back(node2);
    } else if (op == NodeOp::INVALIDATE){
    } else {
        SkDebugf("error op invoke: %d", op);
    }
}

void NodeStatusModify::pushTo(NodeOp op, napi_value node, const SkVector &vector) {
    fOps.push_back(op);
    fNodes.push_back(node);
    fNumbers.push_back(vector.x());
    fNumbers.push_back(vector.y());
}

void NodeStatusModify::pushTo(NodeOp op, napi_value node, const SkRect &rect) {
    fOps.push_back(op);
    fNodes.push_back(node);
    fNumbers.push_back(rect.left());
    fNumbers.push_back(rect.top());
    fNumbers.push_back(rect.right());
    fNumbers.push_back(rect.bottom());
}

void NodeStatusModify::pushTo(NodeOp op, napi_value node, const SkMatrix &matrix) {
    fOps.push_back(op);
    fNodes.push_back(node);
    float m[16] = {
        matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY], 0, matrix[SkMatrix::kMPersp0],
        matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY], 0, matrix[SkMatrix::kMPersp1],
        0, 0, 1, 0,
        matrix[SkMatrix::kMTransX], matrix[SkMatrix::kMTransY], 0, matrix[SkMatrix::kMPersp2]
    };
    fNumbers.insert(fNumbers.end(), m, m + 16);
}

void NodeStatusModify::pushTo(NodeOp op, napi_value node, const SkRRect &rrect) {
    fOps.push_back(op);
    fNodes.push_back(node);
    fNumbers.push_back(rrect.rect().left());
    fNumbers.push_back(rrect.rect().top());
    fNumbers.push_back(rrect.rect().right());
    fNumbers.push_back(rrect.rect().bottom());
    fNumbers.push_back(rrect.radii(SkRRect::kUpperLeft_Corner).x());
    fNumbers.push_back(rrect.radii(SkRRect::kUpperLeft_Corner).y());
    fNumbers.push_back(rrect.radii(SkRRect::kUpperRight_Corner).x());
    fNumbers.push_back(rrect.radii(SkRRect::kUpperRight_Corner).y());
    fNumbers.push_back(rrect.radii(SkRRect::kLowerRight_Corner).x());
    fNumbers.push_back(rrect.radii(SkRRect::kLowerRight_Corner).y());
    fNumbers.push_back(rrect.radii(SkRRect::kLowerLeft_Corner).x());
    fNumbers.push_back(rrect.radii(SkRRect::kLowerLeft_Corner).y());
}

void NodeStatusModify::pushTo(NodeOp op, napi_value node, const SkPath &path) {
    fOps.push_back(op);
    fNodes.push_back(node);
    fStrs.push_back(SkParsePath::ToSVGString(path));
}